import datetime
import re

from dateutil import tz

TZ_CN = tz.gettz('Asia/Shanghai')
TZ_UTC = tz.gettz('UTC')


class Time:
    @staticmethod
    def get_datetime_from_text(text: str,
                               parse_failed_datetime=datetime.datetime(year=1000, month=1, day=1)) -> datetime:
        '''
            文本中提取datetime类型数据
                应用场景:1.Angel提取链接的地址,使用时间排序,时间保存的类型为字符串类型,需要转换
        '''
        from dateutil import parser
        try:
            parsed_time = parser.parse(text, fuzzy=True)
        except:
            parsed_time = parse_failed_datetime
        return parsed_time


class MonthTime:
    @staticmethod
    def get_last_month():
        """
        :return: 2019-9
        """
        return (datetime.datetime.now().replace(day=1) - datetime.timedelta(days=2)).strftime('%Y-%-m')

    @staticmethod
    def get_last_month_2():
        """
        :return: 2019-09
        """
        return (datetime.datetime.now().replace(day=1) - datetime.timedelta(days=2)).strftime('%Y-%-m')

    @staticmethod
    def get_last_month_int():
        return (datetime.datetime.now().replace(day=1) - datetime.timedelta(days=2)).month

    @staticmethod
    def get_this_month():
        now = datetime.datetime.now()
        return str(now.year) + "-" + str(int(now.strftime("%m")))

    @staticmethod
    def get_month_before_last_month():
        return (((datetime.datetime.now().replace(day=1) - datetime.timedelta(days=2))).replace(
            day=1) - datetime.timedelta(
            days=2)).strftime('%Y-%-m')

    @staticmethod
    def get_next_month():
        return (datetime.datetime.now().replace(day=25) + datetime.timedelta(days=15)).strftime('%Y-%-m')

    @staticmethod
    def get_month_begin_time(month=None, parse_utc=False):
        """
            获取某月开始时间
        :param parse_utc: 是否转化为utc时间
        :return:
        """
        if not month:
            month = MonthTime.get_this_month()
        t = datetime.datetime.strptime(month, '%Y-%m')
        return t.astimezone(TZ_UTC) if parse_utc else t

    @staticmethod
    def get_next_month_str(month=None):
        if not month:
            month = MonthTime.get_this_month()
        year, month = month.split('-')
        year = int(year)
        month = int(month)
        if month == 12:
            year += 1
            month = 1
        else:
            month += 1
        return '%d-%d' % (year, month)

    @staticmethod
    def get_month_range(month, parse_utc=False):
        month_begin_time = MonthTime.get_month_begin_time(month, parse_utc=parse_utc)
        next_month_begin_time = MonthTime.get_month_begin_time(MonthTime.get_next_month_str(month), parse_utc=parse_utc)
        return month_begin_time, next_month_begin_time

    @staticmethod
    def _parse_date(text):
        ret = re.findall("(\d+)", text)
        if not ret:
            return
        if len(ret) < 2:
            return
        year = int(ret[0])
        month = int(ret[1])

        # 对提取出来的年份以及月份进行约束:不符合就淘汰
        if len(str(year)) == 2:
            year = int("20" + str(year))
        if len(str(year)) not in [2, 4]:
            return
        if month < 0 or month > 12 or len(str(month)) not in [1, 2]:
            return
        return datetime.datetime(year=year, month=month, day=1)

    @staticmethod
    def get_month_from_text(text, min_datetime=None, max_datetime=None):
        '''
            1.优先使用算法解析月份,其次使用第三方库解析月份:1.模糊解析  2.年份优先解析   问题:第三方库无法解析: 19年 12 月 1 日 情况无法解析
            2.第三方库无法解析的内容使用算法解析(优先使用算法解析):
                1.数值提取,必须有两个数值
                2.年份优先提取,默认第一个为年份,第二个为月份
                3.年份简写为 19 或 20 需要支持解析规则
            3.设置最小,最大解析出来的datetime,如果解析出来的datetime小于或大于该datetime则认为无法解析
        '''
        from dateutil import parser
        text = "".join([i for i in text if i != " "])  # 进行空格去除,提高第三方库以及后续算法的准确率
        parsed_time = None
        if MonthTime._parse_date(text):
            parsed_time = MonthTime._parse_date(text)
        if not parsed_time:
            try:
                parsed_time = parser.parse(text, fuzzy=True)
            except:
                pass

        if parsed_time and parsed_time < max_datetime and parsed_time > min_datetime:
            return "{}-{}".format(parsed_time.year, parsed_time.month)


class WeekTime:

    @staticmethod
    def get_certain_week(time: datetime):
        """
        得到给定时间当前的周
        :return:
        """
        week = datetime.date.isocalendar(time)
        certain_week = "{}-{}".format(week[0], week[1])
        return certain_week

    @staticmethod
    def get_current_and_last_week(parse_utc=False):
        """

        :return:本周日期:2019-39     上周日期:2019-38
        """
        if parse_utc:
            current = datetime.datetime.utcnow()
            last_week = datetime.datetime.utcnow() - datetime.timedelta(weeks=1)
        else:
            current = datetime.datetime.now()
            last_week = datetime.datetime.now() - datetime.timedelta(weeks=1)
        current_week = WeekTime.get_certain_week(current)
        last_week = WeekTime.get_certain_week(last_week)
        return current_week, last_week

    @staticmethod
    def get_current_week():
        """
        得到本周日期
        :param self:
        :return:
        """
        current = datetime.datetime.utcnow()
        current_week = WeekTime.get_certain_week(current)
        return current_week

    @staticmethod
    def get_last_week():
        """
        得到上周日期
        :param self:
        :return:
        """
        last_week = datetime.datetime.utcnow() - datetime.timedelta(weeks=1)
        last_week = WeekTime.get_certain_week(last_week)
        return last_week

    @staticmethod
    def get_last_three_weeks(week_str):
        """
        根据当前周得到上面三周的日期格式
        :param week_str:
        :return:
        """
        week_offset = week_str.split("-")[1]
        week_offset = int(week_offset)

        str_week = WeekTime.get_certain_week(datetime.datetime.utcnow() - datetime.timedelta(weeks=week_offset))
        str_last_week = WeekTime.get_certain_week(
            datetime.datetime.utcnow() - datetime.timedelta(weeks=week_offset + 1))
        str_the_week_before_last = WeekTime.get_certain_week(
            datetime.datetime.utcnow() - datetime.timedelta(weeks=week_offset + 2))
        return str_week, str_last_week, str_the_week_before_last

    @staticmethod
    def get_this_month():
        now = datetime.datetime.now()
        this_month = str(now.year) + "-" + str(int(now.strftime("%m")))
        return this_month

    @staticmethod
    def get_time_range(week):
        """
            凭周计算时间段
            当年第一天必为第一周。
            当年第一周结束 +
        :param week:
        :return:
        """
        year, week_num = [int(i) for i in week.split('-')]
        first_day_weekday = datetime.datetime(year, 1, 1, 0, 0, 0)
        first_week_lastday = first_day_weekday + datetime.timedelta(days=7 - first_day_weekday.weekday())
        if week_num == 1:
            week_end_time = first_week_lastday
            week_begin_time = week_end_time - datetime.timedelta(days=7)
        else:
            week_begin_time = first_week_lastday + datetime.timedelta(days=7 * (week_num - 2))
            week_end_time = first_week_lastday + datetime.timedelta(days=7 * (week_num - 1))
        return week_begin_time.replace(tzinfo=TZ_CN).astimezone(TZ_UTC), week_end_time.replace(tzinfo=TZ_CN).astimezone(
            TZ_UTC)

    @staticmethod
    def get_week_start_end_time(time_type="date", is_utc=True, default_time=datetime.date.today()):
        """
        本周开始结束日期
        :return:
        """
        import datetime
        # 开始日期
        start = default_time - datetime.timedelta(days=default_time.weekday())
        # 结束日期
        end = default_time - datetime.timedelta(days=default_time.weekday() - 6)
        if time_type == "date":
            return start, end
        elif time_type == "datetime":
            if is_utc:
                start = start + datetime.timedelta(hours=8)
                start = datetime.datetime.strptime(str(start), '%Y-%m-%d') - datetime.timedelta(hours=8)
                end = datetime.datetime.strptime(str(end), '%Y-%m-%d') + datetime.timedelta(hours=16)
            else:
                start = datetime.datetime.strptime(str(start), '%Y-%m-%d')
                end = datetime.datetime.strptime(str(end), '%Y-%m-%d') + datetime.timedelta(hours=24)
            return start, end


class QuarterTime:
    @staticmethod
    def get_this_quarter():
        now_ = datetime.datetime.now()
        return '%d-Q%d' % (now_.year, (now_.month + 2) / 3)

    @staticmethod
    def get_quarter_range(q):
        year, quarter = q.split('-Q')
        year = int(year)
        quarter = int(quarter)
        m_begin, m_end = quarter * 3 - 2, quarter * 3 + 1
        return datetime.datetime(year, m_begin, 1, 0, 0, 0), \
               datetime.datetime(year, m_end, 1, 0, 0, 0) - datetime.timedelta(days=1) if m_end != 13 \
                   else datetime.datetime(year, 12, 31, 0, 0, 0)
